﻿using System;
using System.Net;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
using SignalRClient = Microsoft.AspNet.SignalR.Client;

namespace AisinGioro.WinAgent
{
    using Core;
    using Core.DTOs;
    using Core.WinForm;

    public partial class MainForm : Form
    {
        MaskLoading _maskLoading;
        ClientManager _client;
        SystemInfo _si;
        NetworkInfo _ni;
        CancellationTokenSource cancelTokenSource = null;
        
        public MainForm()
        {
            _si = new SystemInfo();
            _ni = new NetworkInfo();            

            InitializeComponent();
            
            this.Controls.Add(_maskLoading= new MaskLoading());
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            this.nmrPort.Value = 9000;
        }

        #region 标题栏拖动
        [DllImport("user32.dll")]
        public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wparam, int lparam);
        private void menuStrip1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left) //按下的是鼠标左键
            {
                Capture = false; //释放鼠标使能够手动操作
                SendMessage(Handle, 0x00A1, 2, 0); //拖动窗体
            }
        }
        #endregion

        #region 窗体显示状态控制
        private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            this.toolStripMenuItemOpen_Click(this.toolStripMenuItemOpen, e);
        }
        private void toolStripMenuItemOpen_Click(object sender, EventArgs e)
        {
            this.ShowInTaskbar = true;  //显示在系统任务栏
            this.WindowState = FormWindowState.Normal;  //还原窗体
            this.BringToFront();
            //notifyIcon.Visible = false;  //托盘图标隐藏
        }
        private void toolStripMenuItemExit_Click(object sender, EventArgs e)
        {
            this.btnFormClose_Click(this.btnFormClose, EventArgs.Empty);
        }
        private void btnFormHidden_Click(object sender, EventArgs e)
        {
            this.WindowState = FormWindowState.Minimized;
            this.ShowInTaskbar = false;
        }
        private void btnFormClose_Click(object sender, EventArgs e)
        {
            if (cancelTokenSource != null)
            {
                if (!cancelTokenSource.IsCancellationRequested)
                    cancelTokenSource.Cancel();

                cancelTokenSource.Dispose();
            }

            if (_client != null)
                _client.Stop();

            this.notifyIcon.Visible = false;
            this.Close();
            this.Dispose();
            Environment.Exit(Environment.ExitCode);
        }
        #endregion

        #region 连接/断开主机服务
        private void btnServConnect_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(this.tbxIPAddress.Text))
            {
                MessageBox.Show("Please enter the IP address.");
                this.tbxIPAddress.Focus();
                return;
            }
            IPAddress address;
            if (!IPAddress.TryParse(this.tbxIPAddress.Text, out address))
            {
                MessageBox.Show("The format of the IP address is wrong.");
                this.tbxIPAddress.Focus();
                return;
            }
            if (string.IsNullOrEmpty(this.tbxHostId.Text))
            {
                MessageBox.Show("Please enter the Host id.");
                this.tbxHostId.Focus();
                return;
            }

            var hostUrl = $"http://{address.ToString()}:{this.nmrPort.Value}/signalr";

            if (_client == null || !_client.HostUrl.Equals(hostUrl, StringComparison.OrdinalIgnoreCase))  //注意：如果不加null判断，断开后重连可能会超时 [Transport timed out trying to connect]
            {
                _client = new ClientManager(hostUrl);
                _client.OnStartSucceed += _client_OnStartSucceed;
                _client.OnStopSucceed += _client_OnStopSucceed;
                _client.OnFailed += _client_OnFailed;
                _client.StateChanged = _client_StateChanged;
            }
            _client.Start();
            _maskLoading.ShowLoad();
        }

        private void btnServDisconnect_Click(object sender, EventArgs e)
        {
            if (cancelTokenSource != null)
            {
                if (!cancelTokenSource.IsCancellationRequested)
                    cancelTokenSource.Cancel();
            }

            if (_client != null)
                _client.Stop();
        }

        private void _client_OnStartSucceed(object sender, EventArgs e)
        {
            WinFormHelper.SetControlCrossThread(this.Container1, () =>
            {
                this.btnServConnect.Enabled = false;
                this.btnServDisconnect.Enabled = true;
                this.tbxIPAddress.Enabled = false;
                this.nmrPort.Enabled = false;
                this.tbxHostId.Enabled = false;
                _maskLoading.HideLoad();
            });
            MessageBox.Show("Server connected! " + _client.HostUrl);

            cancelTokenSource = new CancellationTokenSource();

            var hostId = this.tbxHostId.Text.Trim();

            //推送系统状态

            Task.Factory.StartNew(() =>
            {
                SubmitHeartbeatAsync(hostId);
                SubmitCPUInfoAsync(hostId);
                SubmitMemoryInfoAsync(hostId);
                SubmitDiskInfoAsync(hostId);
                SubmitCurrentConnectionAsync(hostId);
            }, cancelTokenSource.Token);
        }

        private void _client_OnStopSucceed(object sender, EventArgs e)
        {
            WinFormHelper.SetControlCrossThread(this.Container1, () =>
            {
                this.btnServConnect.Enabled = true;
                this.btnServDisconnect.Enabled = false;
                this.tbxIPAddress.Enabled = true;
                this.nmrPort.Enabled = true;
                this.tbxHostId.Enabled = true;
                _maskLoading.HideLoad();
            });

            //MessageBox.Show("Server disconnected!");
        }

        private void _client_OnFailed(object sender, ErrorOccuredEventArgs e)
        {
            WinFormHelper.SetControlCrossThread(_maskLoading, () =>
            {
                _maskLoading.HideLoad();
            });
            
            MessageBox.Show(e.Error.GetBaseException().Message, "Error");
        }

        private void _client_StateChanged(SignalRClient.ConnectionState state)
        {
            switch (state)
            {
                case SignalRClient.ConnectionState.Connecting:
                    WinFormHelper.SetControlCrossThread(this, () => {
                        this.lblConnStatus.Text = "Connecting to server...";
                        this.lblConnStatus.ForeColor = SystemColors.GrayText;
                    });
                    LogHelper.Trace("Connecting to server...{0}", _client.HostUrl); break;
                case SignalRClient.ConnectionState.Connected:
                    WinFormHelper.SetControlCrossThread(this, () => {
                        this.lblConnStatus.Text = "Connected";
                        this.lblConnStatus.ForeColor = Color.DarkCyan;
                    });
                    LogHelper.Trace("Server is connected"); break;
                case SignalRClient.ConnectionState.Reconnecting:
                    WinFormHelper.SetControlCrossThread(this, () => {
                        this.lblConnStatus.Text = "Reconnecting to server...";
                        this.lblConnStatus.ForeColor = SystemColors.GrayText;
                    });
                    LogHelper.Trace("Reconnecting to server..."); break;
                case SignalRClient.ConnectionState.Disconnected:
                    WinFormHelper.SetControlCrossThread(this, () => {
                        this.lblConnStatus.Text = "Disconnected";
                        this.lblConnStatus.ForeColor = Color.DarkRed;
                        this.btnServDisconnect_Click(this.btnServDisconnect, EventArgs.Empty);
                    });
                    LogHelper.Trace("Server is disconnected"); break;
            }
        }
        #endregion

        #region 功能事件
        
        private async void SubmitHeartbeatAsync(string hostId, int minInterval = 1500, int maxInterval = 2500)
        {
            while (!cancelTokenSource.IsCancellationRequested)
            {
                await _client.SubmitSysInfo(new HeartbeatDto
                {
                    HostId = hostId,
                    IsAlive = true
                });
                var delay = new Random(DateTime.Now.Millisecond).Next(minInterval, maxInterval);
                await Task.Delay(delay);
            }
        }
        private async void SubmitCPUInfoAsync(string hostId, int minInterval = 2500, int maxInterval = 3500)
        {
            while (!cancelTokenSource.IsCancellationRequested)
            {
                await _client.SubmitSysInfo(new CPUInfoDto
                {
                    HostId = hostId,
                    ProcessorCount = _si.ProcessorCount,
                    CpuLoad = Math.Round(_si.CpuLoad, 1)
                });
                var delay = new Random(DateTime.Now.Millisecond).Next(minInterval, maxInterval);
                await Task.Delay(delay);
            }
        }
        private async void SubmitDiskInfoAsync(string hostId, int minInterval = 15000, int maxInterval = 25000)
        {
            while (!cancelTokenSource.IsCancellationRequested)
            {
                var dto = new DiskInfoDto
                {
                    HostId = hostId
                };
                _si.GetLogicalDrives().ForEach((disk) =>
                {
                    var dtoItem = new DiskInfoItem
                    {
                        Label = disk.Name,
                        Size = Math.Round(disk.Size / 1024.0 / 1024.0 / 1024.0, 1),
                        FreeSpace = Math.Round(disk.FreeSpace / 1024.0 / 1024.0 / 1024.0)
                    };
                    dtoItem.DiskLoad = Math.Round((dtoItem.Size - dtoItem.FreeSpace) * 100 / (dtoItem.Size * 1.0), 1);
                    dto.Items.Add(dtoItem);
                });
                await _client.SubmitSysInfo(dto);
                var delay = new Random(DateTime.Now.Millisecond).Next(minInterval, maxInterval);
                await Task.Delay(delay);
            }
        }
        private async void SubmitMemoryInfoAsync(string hostId, int minInterval = 3000, int maxInterval = 4000)
        {            
            while (!cancelTokenSource.IsCancellationRequested)
            {
                var dto = new MemoryInfoDto
                {
                    HostId = hostId,
                    Physical = Math.Round(_si.PhysicalMemory / 1024.0 / 1024.0 / 1024.0, 1),
                    Available = Math.Round(_si.MemoryAvailable / 1024.0 / 1024.0 / 1024.0, 1)
                };
                dto.MemoryLoad = Math.Round((dto.Physical - dto.Available) * 100 / (dto.Physical * 1.0), 1);
                await _client.SubmitSysInfo(dto);
                var delay = new Random(DateTime.Now.Millisecond).Next(minInterval, maxInterval);
                await Task.Delay(delay);
            }
        }
        private async void SubmitCurrentConnectionAsync(string hostId, int minInterval = 4000, int maxInterval = 6000)
        {
            while (!cancelTokenSource.IsCancellationRequested)
            {
                var dto = new CurrentConnectionDto
                {
                    HostId = hostId
                };
                foreach (var item in _ni.GetCurrentConnections())
                {
                    dto.Items.Add(new ConnectionItem
                    {
                        SiteName = item.Key,
                        Total = item.Value
                    });
                }
                await _client.SubmitSysInfo(dto);
                var delay = new Random(DateTime.Now.Millisecond).Next(minInterval, maxInterval);
                await Task.Delay(delay);
            }
        }

        #endregion
    }
}
